home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
EnigmA Amiga Run 1997 May
/
EnigmA AMIGA RUN 18 (1997)(G.R. Edizioni)(IT)[!][issue 1997-05][EAR-CD II].iso
/
earcd
/
comm
/
misc
/
new8n1.lha
/
8n1_020.s
< prev
next >
Wrap
Text File
|
1997-02-18
|
60KB
|
1,905 lines
********************************************************************************
** **
** Name : 8n1_020.s **
** Copyright : © Copyright 96-97 **
** Author : Iain Barclay **
** Created : 18 Feb 97 **
** Version : 37.31 **
** **
********************************************************************************
;
; SNMA specific options
;
IFD SNMA
;
CPU M68030
;
SNMAOPT Q,A,M,T,E-,P,R,B
;
ELSE
; I have phxopts define this symbol
IFD PHXASS
machine 68030
OPT !
ENDC
ENDC
;
;
NEWCODE SET 1
SETDSR SET 0
NCOMM SET 0
STATS SET 0
;
;
;
SECTION text,CODE
;
;
;
DEVICES_SERIAL_I_OBSOLETE EQU 1
;
;
;
INCLUDE "exec/lists.i"
INCLUDE "exec/memory.i"
INCLUDE "exec/resident.i"
INCLUDE "exec/devices.i"
INCLUDE "exec/execbase.i"
INCLUDE "exec/io.i"
INCLUDE "exec/ports.i"
INCLUDE "exec/errors.i"
INCLUDE "exec/initializers.i"
INCLUDE "intuition/preferences.i"
INCLUDE "devices/timer.i"
INCLUDE "devices/serial.i"
INCLUDE "hardware/custom.i"
INCLUDE "hardware/cia.i"
INCLUDE "hardware/intbits.i"
INCLUDE "hardware/adkbits.i"
INCLUDE "resources/misc.i"
INCLUDE "exec/alerts.i"
INCLUDE "exec/macros.i"
INCLUDE "8n1.device_rev.i"
;
; Define hardware references
;
XREF _custom
XREF _intena,_intenar,_intreq,_intreqr
XREF _ciab,_ciabpra
XREF _serper,_serdat,_serdatr
XREF _adkcon,_adkconr
;
; Exec Functions
;
XREF _LVORemDevice,_LVOOpenDevice,_LVOCloseDevice
XREF _LVOSupervisor
XREF _LVOAllocMem,_LVOFreeMem,_LVOCopyMem
XREF _LVOReplyMsg
XREF _LVOSendIO,_LVOAbortIO
XREF _LVODisable,_LVOEnable
XREF _LVOFindName
XREF _LVOOpenResource
XREF _LVOAddIntServer,_LVORemIntServer
XREF _LVOAlert
XREF _LVOOpenLibrary,_LVOCloseLibrary
;
; Misc Resource Functions
;
XREF _LVOAllocMiscResource,_LVOFreeMiscResource
;
; Intuition Functions
;
XREF _LVOGetPrefs
;
; Mask used to get rid of the printer bits.
;
PRTMASK EQU (CIAF_PRTRPOUT|CIAF_PRTRBUSY)
;
; Autovector offsets
;
LVL1VEC EQU (1-1)*4+$64
LVL5VEC EQU (5-1)*4+$64
;
; New Style device stuff (not the best way but it will have too do for now)
; If u don't have New Style stuff defined elsewhere remove these ;
;NSCMD_DEVICEQUERY EQU $4000
; STRUCTURE NSDEVICEQUERYRESULT,0
; ULONG DEVQUERYFORMAT
; ULONG SIZEAVAILABLE
; UWORD DEVICETYPE
; UWORD DEVICESUBTYPE
; APTR SUPPORTEDCOMMANDS
; LABEL NSDEVICEQUERYRESULT_SIZE
;NSDEVTYPE_SERIAL EQU 11
;
; Device base
;
STRUCTURE Base8n1,LIB_SIZE
UBYTE vb_SaveDDRA
UBYTE vb_SavePRA
APTR vb_MiscBase
APTR vb_OldLevel1
APTR vb_OldLevel5
ULONG vb_SegList
ULONG vb_DefBaud
ULONG vb_DefRBufLen
ULONG vb_CurRBuf
ULONG vb_CurRBufLen
ULONG vb_CurBaud
UBYTE vb_SerFlags
UBYTE vb_Initialized
LABEL sizeof_Base8n1
;
;
;
IFNE STATS
XREF _KPrintF
XREF KPrintF
ENDC
;
;
;
Start:
moveq #-1,d0 ; set return code
rts ; return
;
; RamLib looks for this romtag
;
ROMTag DC.W RTC_MATCHWORD ; RT_MATCHWORD
DC.L ROMTag ; RT_MATCHTAG
DC.L ENDTag ; RT_ENDSKIP
DC.B RTF_AUTOINIT ; RT_FLAGS
DC.B VERSION ; RT_VERSION
DC.B NT_DEVICE ; RT_TYPE
DC.B 0 ; RT_PRI
DC.L Name ; RT_NAME
DC.L IdString ; RT_IDSTRING
DC.L Init ; RT_INIT
;
; Perform device initialization
;
cnop 0,4 ; align for 020+
InitRoutine:
exg d0,a0 ; swap seglist and base
move.l d0,vb_SegList(a0) ; store seglist in base
move.l a6,SysBase ; store in global storage
btst #1,AttnFlags(a6) ; test for 020+ #3 = 040, #4 = FPU, #6 = FPU040
beq.b .ok
move.l a1,-(sp)
move.l d0,a0
move.l d0,a1
moveq #0,d0 ; clear
move.w LIB_NEGSIZE(a0),d0 ; calculate
suba.l d0,a1 ; memory address
add.w LIB_POSSIZE(a0),d0 ; and size
jsr _LVOFreeMem(a6) ; free it
move.l (sp)+,a1
moveq #0,d0 ; clear
bra.b .rt
.ok exg a0,d0 ; swap them back
.rt rts ; return
;
;
;
cnop 0,4 ; align for 020+
dev_Open: ; (a1 iorequest, a5 will be Base8n1, a6 sysbase )
move.l a5,-(sp) ; save registers
movea.l a6,a5 ; save base
movea.l SysBase(pc),a6 ; get ExecBase
;
cmp.w #IOEXTSER_SIZE,MN_LENGTH(a1) ; IORequest size >= IOEXTSER_SIZE
bcs 50$ ; nope, finish
;
tst.l d0 ; unit 0 specified?
bne 50$ ; nope, error (who cares?)
;
tst.w LIB_OPENCNT(a5) ; currently open?
bne 10$ ; yep, go process
;
move.b IO_SERFLAGS(a1),vb_SerFlags(a5) ; save flags
;
tst.b vb_Initialized(a5) ; already initialized?
bne 40$ ; yep, skip initialization
;
move.l a1,-(sp) ; save register
;
lea.l miscresource(pc),a1 ; ptr to resource name
jsr _LVOOpenResource(a6) ; go open it
move.l d0,vb_MiscBase(a5) ; save base
;
moveq #MR_SERIALPORT,d0 ; set unit number
bsr allocResource ; go allocate it
tst.l d0 ; did we get it?
bne.b 20$ ; nope, error
;
moveq #MR_SERIALBITS,d0 ; set unit number
bsr allocResource ; go allocate it
tst.l d0 ; did we get it?
bne.b 5$ ; nope, error
;
; Get system preferences
;
move.l a1,-(sp) ; save registers
;
lea.l intuitlib(pc),a1 ; ptr to library name
moveq #0,d0 ; can't use OldOpenLibrary as this will not be supported.
jsr _LVOOpenLibrary(a6) ; go open it (any version)
movea.l d0,a6 ; get intuition base
;
move.l #(pf_SerParShk+3)&$fffffffc,d0 ; size we need (aligned)
suba.l d0,sp ; reserve space
;
movea.l sp,a0 ; set data area ptr
jsr _LVOGetPrefs(a6) ; get preferences
;
movea.l a6,a1 ; get intuition base
movea.l SysBase(pc),a6 ; restore ExecBase
jsr _LVOCloseLibrary(a6) ; close it
;
moveq #$0f,d1 ; set mask
and.b pf_SerStopBuf(sp),d1 ; get bufsize index
addq.l #8,d1 ; calc shift value
moveq #2,d0 ; get 1<<1
lsl.l d1,d0 ; get default bufsize
move.l d0,vb_DefRBufLen(a5) ; and store
;
moveq #0,d1 ; clear upper half
move.w pf_BaudRate(sp),d1 ; get baud rate
move.w .baudTable(pc,d1.l*2),d1 ; get baud rate
move.l d1,vb_DefBaud(a5) ; and store
;
bsr internalReset ; go init baud and buffer
;
lea.l (pf_SerParShk+3)&$fffffffc(sp),sp ; restore stack
movea.l (sp)+,a1 ; restore registers
tst.l d0 ; got 'em?
beq.b 30$ ; yep, branch
;
moveq #MR_SERIALBITS,d0 ; set unit
movea.l vb_MiscBase(a5),a6 ; get MiscBase
jsr _LVOFreeMiscResource(a6) ; release the resource
;
5$ moveq #MR_SERIALPORT,d0 ; set unit
movea.l vb_MiscBase(a5),a6 ; get MiscBase
jsr _LVOFreeMiscResource(a6) ; release the resource
movea.l SysBase(pc),a6 ; restore ExecBase
;
20$ moveq #SerErr_DevBusy,d0 ; set error status
bra 50$ ; go return
;
; Preferences baud lookup table
;
cnop 0,4 ; align for 020+
.baudTable:
dc.w 112,300,1200,2400,4800,9600,19200,31250,38400,57600,62400,64800
;
cnop 0,4 ; align for 020+
30$ moveq #0,d1 ; clear flags
lea.l timerReq(pc),a1 ; ptr to timer request
moveq #UNIT_VBLANK,d0 ; set unit
lea.l timerdevice(pc),a0 ; ptr to device name
jsr _LVOOpenDevice(a6) ; go open it
;
jsr _LVODisable(a6) ; disable interrupts
;
lea.l _ciab,a1 ; get ptr to ciab
move.b ciaddra(a1),d0
move.b d0,vb_SaveDDRA(a5) ; save DDR value
andi.b #PRTMASK,d0 ; make serial bits input
move.b (a1),d1 ; ciapra(a1)
ori.b #CIAF_COMDTR|CIAF_COMRTS,d0 ; make DTR/RTS output
move.b d1,vb_SavePRA(a5) ; save PR value
andi.b #CIAF_COMDTR|CIAF_COMRTS|PRTMASK,d0 ; turn on DTR/RTS
move.b d0,ciaddra(a1)
andi.b #CIAF_COMCTS|CIAF_COMDSR|PRTMASK,d1 ; make CTS/DSR input
move.b d1,(a1) ; ciapra(a1)
;
moveq #INTB_PORTS,d0 ; get interrupt number
lea.l VBInterrupt(pc),a1 ; get interrupt ptr
jsr _LVOAddIntServer(a6) ; add it to the list
;
bsr getVBR ; get vector base (in A0)
;
move.l LVL1VEC(a0),vb_OldLevel1(a5) ; save original vector
lea.l level1(pc),a1 ; get new vector ptr
move.l a1,LVL1VEC(a0) ; set new vector
;
move.l LVL5VEC(a0),vb_OldLevel5(a5) ; save original vector
lea.l level5(pc),a1 ; get new vector ptr
move.l a1,LVL5VEC(a0) ; set new vector
;
lea.l _custom,a1 ; get ptr to custom chips
move.w #INTF_RBF|INTF_TBE,intreq(a1) ; clear pending interrupts
move.w #INTF_SETCLR|INTF_RBF|INTF_TBE,intena(a1) ; enable RBF & TBE
;
addq.b #1,vb_Initialized(a5) ; set flag
;
jsr _LVOEnable(a6) ; enable interrupts
;
movea.l (sp)+,a1 ; restore register
;
bra.b 40$ ; go exit
;
cnop 0,4 ; align for 020+
10$ moveq #SerErr_DevBusy,d0 ; preset error status
btst #SERB_SHARED,vb_SerFlags(a5) ; opened shared?
beq.b 50$ ; nope, error
btst #SERB_SHARED,IO_SERFLAGS(a1) ; requesting shared?
beq.b 50$ ; nope, error
;
; Initialize I/O request
;
40$ moveq #8,d0 ; get char size
move.b d0,IO_READLEN(a1) ; set read length
move.b d0,IO_WRITELEN(a1) ; set write length
moveq #1,d0 ; get stop bits
move.b d0,IO_STOPBITS(a1) ; set stop bits
move.b IO_SERFLAGS(a1),d0
ori.b #SERF_XDISABLED|SERF_RAD_BOOGIE|SERF_QUEUEDBRK|SERF_7WIRE,d0 ;flags
andi.b #~(SERF_PARTY_ODD|SERF_PARTY_ON),d0 ; not used
move.b d0,IO_SERFLAGS(a1)
move.l vb_CurBaud(a5),IO_BAUD(a1) ; set baud
move.l vb_CurRBufLen(a5),IO_RBUFLEN(a1) ; set read buffer length
;
addq.w #1,LIB_OPENCNT(a5) ; incr open count
andi.b #~(1<<LIBB_DELEXP),LIB_FLAGS(a5) ; clear expunge bit
moveq #0,d0 ; no error
;
50$ move.b d0,IO_ERROR(a1) ; store error code
movea.l a5,a6 ; restore base
move.l (sp)+,a5 ; restore registers
rts ; return
;
; Attempt to allocate one of the serial resources.
;
cnop 0,4 ; align for 020+
allocResource:
move.l a6,-(sp) ; save base pointer
move.l d0,-(sp) ; save unit
lea.l Name(pc),a1 ; get lock name
move.l vb_MiscBase(a5),a6 ; get MiscBase
jsr _LVOAllocMiscResource(a6) ; go allocate it
tst.l d0 ; did we get it?
beq.b 20$ ; yep, branch
;
; It's in use, so we try to locate the device using the string
; returned and attempt to remove it.
;
movea.l SysBase(pc),a6 ; get ExecBase
;
movea.l d0,a1 ; get ptr to serial name
lea.l DeviceList(a6),a0 ; get ptr to device list
jsr _LVOFindName(a6) ; go find it
tst.l d0 ; found?
beq.b 10$ ; nope, branch
;
movea.l d0,a1 ; xfer device ptr
jsr _LVORemDevice(a6) ; remove it
;
; We then retry the allocate.
;
10$ move.l (sp),d0 ; get unit
lea.l Name(pc),a1 ; get lock name
movea.l vb_MiscBase(a5),a6 ; get MiscBase
jsr _LVOAllocMiscResource(a6) ; go allocate it
;
20$ addq.l #4,sp ; restore stack ptr
movea.l (sp)+,a6 ; restore base ptr
rts ; return
cnop 0,4 ; align for 020+
;
; Set exception vectors
;
cnop 0,4 ; align for 020+
getVBR:
move.l a5,-(sp) ; save registers
lea.l 20$(pc),a5 ; ptr to routine
jsr _LVOSupervisor(a6) ; get into supervisor state
10$ movea.l (sp)+,a5 ; restore register
rts ; return
cnop 0,4
20$ movec.l vbr,a0 ; get vector base
rte ; return
;
; Device Close routine
;
cnop 0,4 ; align for 020+
dev_Close:
;
moveq #-1,d0 ; invalidate
move.l d0,IO_DEVICE(a1) ; device
;
tst.w LIB_OPENCNT(a6)
beq 5$
subq.w #1,LIB_OPENCNT(a6) ; decr open count
bne dev_Null ; still open? yep, branch
5$
;
btst #LIBB_DELEXP,LIB_FLAGS(a6)
beq dev_Null
;
; Device Expunge routine (also fall through from dev_Close)
;
dev_Expunge:
ori.b #1<<LIBB_DELEXP,LIB_FLAGS(a6) ; Set expunge flag
tst.w LIB_OPENCNT(a6) ; currently open?
bne dev_Null ; yep, so just exit
;
move.l a5,-(sp) ; save registers
movea.l a6,a5 ; save base
movea.l SysBase(pc),a6 ; get ExecBase
;
move.l a1,-(sp) ; save registers
;
bsr.b getVBR ; get vector base (in A0)
;
moveq #1,d0 ; set not restored code
;
lea.l level1(pc),a1 ; get our vector ptr
cmpa.l LVL1VEC(a0),a1 ; do they match?
bne 10$ ; nope, can't restore
;
lea.l level5(pc),a1 ; get our vector ptr
cmpa.l LVL5VEC(a0),a1 ; do they match?
bne 10$ ; nope, can't restore
;
jsr _LVODisable(a6) ; disable interrupts
;
move.l vb_OldLevel1(a5),LVL1VEC(a0) ; restore original vector
move.l vb_OldLevel5(a5),LVL5VEC(a0) ; restore original vector
;
moveq #INTB_PORTS,d0 ; get interrupt number
lea.l VBInterrupt(pc),a1 ; get interrupt ptr
jsr _LVORemIntServer(a6) ; remove it from the list
;
lea.l _custom,a0 ; get custom base
move.w #INTF_RBF|INTF_TBE,d0 ; indicate serial interrupts
move.w d0,intena(a0) ; disable interrupts
move.w d0,intreq(a0) ; clear pending interrupts
;
bsr freeBuf ; free allocated buffers
;
lea.l _ciab,a0 ; get pointer to ciab
move.b ciaddra(a0),d0 ; get DDR value
andi.b #PRTMASK,d0 ; save printer bits
ori.b #~PRTMASK,d0 ; set serial bits to output
move.b d0,ciaddra(a0) ; store value
;
move.b (a0),d0 ; get PR value ( ciapra(a0) )
andi.b #PRTMASK,d0 ; mask out serial bits
move.b vb_SavePRA(a5),d1 ; get saved value
andi.b #~PRTMASK,d1 ; mask out printer bits
or.b d1,d0 ; combine the two
move.b d0,(a0) ; and store ( ciapra(a0) )
;
move.b ciaddra(a0),d0 ; get DDR value
andi.b #PRTMASK,d0 ; mask out serial bits
move.b vb_SaveDDRA(a5),d1 ; get saved value
andi.b #~PRTMASK,d1 ; mask out printer bits
or.b d1,d0 ; combine the two
move.b d0,ciaddra(a0) ; and store
;
lea.l timerReq(pc),a1 ; get ptr to timer request
jsr _LVOCloseDevice(a6) ; go close it
;
movea.l vb_MiscBase(a5),a6 ; get MiscBase
moveq #MR_SERIALBITS,d0 ; set unit
jsr _LVOFreeMiscResource(a6) ; release the resource
;
moveq #MR_SERIALPORT,d0 ; set unit
jsr _LVOFreeMiscResource(a6) ; release the resource
movea.l SysBase(pc),a6 ; restore ExecBase
;
jsr _LVOEnable(a6) ; enable interrupts
;
subq.b #1,vb_Initialized(a5) ; clear flag
moveq #0,d0 ; free up everything
;
10$ movea.l (sp)+,a1 ; restore registers
;
movea.l a5,a6 ; restore base
movea.l (sp)+,a5 ; restore registers
;
tst.l d0 ; freed?
bne.b dev_Null ; nope, can't expunge, exit
;
clr.b vb_SerFlags(a6) ; clear flags
;
move.l vb_SegList(a6),d0 ; get seglist ptr
move.l d0,-(sp) ; save registers (save D0!)
;
movea.l a6,a1 ; get base
; This is the REMOVE macro
move.l (a1)+,a0
move.l (a1),a1 ; LN_PRED
move.l a0,(a1)
move.l a1,LN_PRED(a0)
;
movea.l a6,a1 ; get base
moveq #0,d0 ; clear work
move.w LIB_NEGSIZE(a6),d0 ; calculate
suba.l d0,a1 ; memory address
add.w LIB_POSSIZE(a6),d0 ; and size
move.l a6,-(sp) ; save registers
movea.l SysBase(pc),a6 ; get ExecBase
jsr _LVOFreeMem(a6) ; free it
;
move.l (sp)+,a6 ; restore registers
move.l (sp)+,d0 ; restore registers
exp_end rts ; return (seglist in D0!)
;
; Device "ExtFunc" routine
;
cnop 0,4 ; align for 020+
dev_Null:
moveq #0,d0 ; set return code
rts ; return
;
;
;
;
cnop 0,4 ; align for 020+
cmdTable dc.w cmd_Invalid-cmdTable ; CMD_INVALID
dc.w cmd_Reset-cmdTable ; CMD_RESET
dc.w cmd_Read-cmdTable ; CMD_READ
dc.w cmd_Write-cmdTable ; CMD_WRITE
dc.w cmd_Invalid-cmdTable ; CMD_UPDATE
dc.w cmd_Clear-cmdTable ; CMD_CLEAR
dc.w cmd_Invalid-cmdTable ; CMD_STOP
dc.w cmd_Invalid-cmdTable ; CMD_START
dc.w cmd_Flush-cmdTable ; CMD_FLUSH
dc.w sdcmd_Query-cmdTable ; SDCMD_QUERY
dc.w sdcmd_Break-cmdTable ; SDCMD_BREAK
dc.w sdcmd_SetParams-cmdTable ; SDCMD_SETPARAMS
endTable dc.w nscmd_DeviceQuery-cmdTable ; NSCMD_DEVICEQUERY
;
; Device BeginIO routine
;
cnop 0,4 ; align for 020+
dev_BeginIO:
move.l a5,-(sp) ; save register
movea.l a6,a5 ; save base
movea.l SysBase(pc),a6 ; get ExecBase
;
move.b #NT_MESSAGE,LN_TYPE(a1) ; set type
clr.b IO_ERROR(a1) ; clear error
;
andi.b #~(IOSERF_QUEUED|IOSERF_ACTIVE),IO_FLAGS(a1) ; clear flags
;
moveq #0,d0
move.w IO_COMMAND(a1),d0 ; get command
add.w d0,d0 ; multiply by 2
cmpi.w #endTable-cmdTable,d0 ; in range?
bhi.b 25$ ; nope, error
;
5$ move.w cmdTable(pc,d0.l),d0 ; get routine offset
jsr cmdTable(pc,d0.l) ; go do it
tst.l d0 ; I/O completed?
bne.b 19$ ; nope, go return
;
10$
btst #IOB_QUICK,IO_FLAGS(a1) ; need to reply?
bne.b 20$ ; nope, branch
15$ jsr _LVOReplyMsg(a6) ; send it back
;
19$ andi.b #~(1<<IOB_QUICK),IO_FLAGS(a1) ; clear quick bit
20$ movea.l a5,a6 ; restore base
movea.l (sp)+,a5 ; restore register
rts ; return
cnop 0,4 ; align for 020+
;
25$ move.w IO_COMMAND(a1),d0 ; get command
cmpi.w #NSCMD_DEVICEQUERY,d0
bne.b 30$
moveq #24,d0
bra.b 5$
cnop 0,4 ; align for 020+
;
30$ move.b #IOERR_NOCMD,IO_ERROR(a1) ; invalid command
bra.b 10$ ; branch
;
; Device AbortIO routine
;
cnop 0,4 ; align for 020+
dev_AbortIO:
move.l a5,-(sp) ; save registers
movea.l a6,a5 ; save base
movea.l SysBase(pc),a6 ; get ExecBase
;
jsr _LVODisable(a6) ; disable interrupts
;
move.b IO_FLAGS(a1),d1 ; get flags
btst #IOSERB_QUEUED,d1 ; queued request?
bne.b 40$ ; yep, branch
;
btst #IOSERB_ACTIVE,d1 ; active request?
beq.b 10$ ; nope, just exit
;
move.w IO_COMMAND(a1),d0 ; get command
subq.w #CMD_READ,d0 ; was it a read?
beq.b 20$ ; yep, go process
;
subq.w #CMD_WRITE-CMD_READ,d0 ; was it a write?
beq.b 30$ ; yep, go process
;
subq.w #SDCMD_BREAK-CMD_WRITE,d0 ; was it a break?
beq.b 30$ ; yep, go process
;
; Fall through or enter from below
;
10$ jsr _LVOEnable(a6) ; enable ints and return
15$ movea.l a5,a6 ; restore base
movea.l (sp)+,a5 ; restore registers
rts ; return
;
; Abort an active read request
;
20$ clr.l cr_IOReq ; no longer active
bra.b 50$ ; go set flags
;
; Abort an active write request
;
cnop 0,4 ; align for 020+
30$ clr.l cw_Length ; no longer active
clr.l cw_IOReq ; no longer active
move.l cw_Buffer(pc),d0 ; get buffer ptr
sub.l IO_DATA(a1),d0 ; calc number of bytes xfer'd
move.l d0,IO_ACTUAL(a1) ; store
;
; Force a TBE interrupt to get the next write going.
;
move.w #INTF_SETCLR|INTF_TBE,_intreq ; make TBE pending
bra.b 50$ ; go set flags
;
; Remove I/O from queue.
;
cnop 0,4 ; align for 020+
40$ move.l a1,-(sp) ; save ptr
; This is the REMOVE macro
move.l (a1)+,a0
move.l (a1),a1 ; LN_PRED
move.l a0,(a1)
move.l a1,LN_PRED(a0)
;
movea.l (sp)+,a1 ; restore ptr
;
; Set error and return I/O
;
50$ move.b #IOERR_ABORTED,IO_ERROR(a1) ; set error code
move.b IO_FLAGS(a1),d1 ; get flags
ori.b #1<<IOSERB_ABORT,d1 ; set abort flag
andi.b #~(IOSERF_QUEUED|IOSERF_ACTIVE),d1 ; clear flags
move.b d1,IO_FLAGS(a1) ; store flags
btst #IOB_QUICK,d1 ; need to reply?
bne.b 10$ ; nope, branch
jsr _LVOReplyMsg(a6) ; send it back
bra.b 10$ ; branch to return
;
; Abort all active/queued commands and reset internal state
;
cnop 0,4 ; align for 020+
cmd_Reset:
move.l a1,-(sp) ; save I/O request
bsr.b cmd_Flush ; go abort queued requests
;
jsr _LVODisable(a6) ; disable interrupts
;
; This must follow the DISABLE
;
exg a5,a6 ; exchange base and ExecBase
;
move.l cr_IOReq(pc),d0 ; active read?
beq.b 10$ ; nope, branch
movea.l d0,a1 ; get I/O request
bsr dev_AbortIO ; go abort it
;
;
;
10$ move.l cw_IOReq(pc),d0 ; active write?
beq.b 20$ ; nope, branch
movea.l d0,a1 ; get I/O request
bsr dev_AbortIO ; go abort it
;
20$ exg a5,a6 ; restore base and ExecBase
movea.l (sp)+,a1 ; restore I/O request
moveq #8,d0 ; get char size
move.b d0,IO_READLEN(a1) ; set read length
move.b d0,IO_WRITELEN(a1) ; set write length
moveq #1,d0 ; get stop bits
move.b d0,IO_STOPBITS(a1) ; set stop bits
move.l vb_DefBaud(a5),IO_BAUD(a1) ; set to default baud
move.l vb_DefRBufLen(a5),IO_RBUFLEN(a1) ; set to default buflen
bsr internalReset ; go set/verify parameters
move.b d0,IO_ERROR(a1) ; set error code
;
; Set RC
;
moveq #0,d0 ; I/O complete
jmp _LVOEnable(a6) ; enable interrupts
; return
;
; Abort all "queued" requests, leaving all active alone.
;
cnop 0,4 ; align for 020+
cmd_Flush:
move.l a2,-(sp) ; save registers
move.l a1,-(sp) ; save registers
jsr _LVODisable(a6) ; disable interrupts
;
; Abort all queued read requests.
;
lea.l readQ(pc),a2 ; get ptr to read queue
bsr.b 20$ ; branch to abort
;
; Abort all queued write requests.
;
lea.l writeQ(pc),a2 ; get ptr to write queue
bsr.b 20$ ; branch to abort
;
; Enable, restore, and return to caller
;
jsr _LVOEnable(a6) ; enable interrupts
move.l (sp)+,a1 ; restore registers
move.l (sp)+,a2 ; restore registers
;
; Set RC and return
;
moveq #0,d0 ; I/O complete
;
; !!!NOTE!!! In a mad attempt to save 2 bytes, this RTS is used
; by the subroutine below. Why waste 'em? B-)
;
10$ rts ; return ( used below too!! )
;
; Subroutine to remove and reply each I/O request.
;
cnop 0,4 ; align for 020+
20$ movea.l a2,a0 ; get list ptr
; This is the REMHEAD macro
25$ move.l (a0),a1
move.l (a1),d0
beq.b 25$
move.l d0,(a0)
exg.l d0,a1
move.l a0,LN_PRED(a1)
;
tst.l d0 ; end of list?
beq.b 10$ ; yep, branch to return
;
movea.l d0,a1 ; get I/O request
andi.b #~(1<<IOSERB_QUEUED),IO_FLAGS(a1) ; no longer queued
moveq #IOERR_ABORTED,d0 ; indicate aborted
move.b d0,IO_ERROR(a1) ; store status
;
jsr _LVOReplyMsg(a6) ; send it back
bra.b 20$ ; continue with next
;
; Process a CMD_READ request.
;
cnop 0,4 ; align for 020+
cmd_Read:
;
; Zero length requests just get returned.
;
clr.l IO_ACTUAL(a1) ; clear bytes read
move.l IO_LENGTH(a1),d0 ; get length and test
beq.b 20$ ; yep, leave
;
; This can be used to circumvent a bug in NComm 3.0 which
; references the buffer even when there was nothing read.
;
IFNE NCOMM
move.l IO_DATA(A1),a0 ; get data pointer
clr.b (a0) ; clear first byte in buffer
ENDC
;
; The disable counter works just like exec's TDNestCnt field. It's
; initialized to -1. After incrementing, if it is 0, then we
; can attempt to process this request immediately. If it's > 0,
; then we're already disabled and we must queue this request.
;
addq.b #1,disableRead ; incr disable count
bgt.b 50$ ; >0, already disabled
;
; If we're already processing an request, this one has to wait
; until that one is done, so go queue it.
;
tst.l cr_IOReq(pc) ; have an active request?
bne.b 50$ ; yep, go queue this one
;
; If we don't have enough bytes to satisfy this request then go
; queue it.
;
cmp.l i_InCnt(pc),d0 ; length > current bytes?
bgt.b 50$ ; yep, go queue it
;
; Setup fields and go copy the data
;
move.l IO_DATA(a1),cr_OutPtr ; get/set output ptr
move.l IO_LENGTH(a1),cr_Length ; get/set output count
bsr copyData ; go copy 'em
moveq #0,d0 ; I/O complete
;
; We're done, so back off the disable counter.
;
10$ subq.b #1,disableRead ; decr disable count
;
; Return to caller
;
20$ rts ; return
;
; Just set flags and queue. The read interrupt will handle it.
;
50$ ori.b #1<<IOSERB_QUEUED,IO_FLAGS(a1) ; indicate queued
;
; Add this request to the end.
;
jsr _LVODisable(a6) ; disable interrupts
lea.l readQ(pc),a0 ; get pointer to read queue
; This is the ADDTAIL macro
addq.l #LH_TAIL,a0
move.l LN_PRED(a0),d0
move.l a1,LN_PRED(a0)
EXG d0,a0
movem.l d0/a0,(a1)
move.l a1,(a0)
;
jsr _LVOEnable(a6) ; enable interrupts
;
; Indicate that this request was not handled immediatly.
;
moveq #1,d0 ; I/O not complete
bra.b 10$ ; branch to return
;
; Process a CMD_WRITE request.
;
cnop 0,4 ; align for 020+
cmd_Write:
clr.l IO_ACTUAL(a1) ; clear bytes written
tst.l IO_LENGTH(a1) ; get length and test
bne.b sdcmd_Break ; nope, proccess
rts
;
; Entry point for Break command and fall through from cmd_Write.
;
cnop 0,4 ; align for 020+
sdcmd_Break:
;
; Just set flags and queue. The TBE interrupt will handle it.
;
ori.b #1<<IOSERB_QUEUED,IO_FLAGS(a1) ; indicate queued
;
; Protect.
;
jsr _LVODisable(a6) ; disable interrupts
;
; Add request to end of queue.
;
lea.l writeQ(pc),a0 ; get queue list ptr
; This is the ADDTAIL macro
addq.l #LH_TAIL,a0
move.l LN_PRED(a0),d0
move.l a1,LN_PRED(a0)
EXG d0,a0
movem.l d0/a0,(a1)
move.l a1,(a0)
;
; If we have an active request, don't force interrupt.
;
tst.l cw_IOReq(pc) ; have an active request?
bne.b 10$ ; yep, branch
;
; Force a TBE interrupt to get the writes going.
;
move.w #INTF_SETCLR|INTF_TBE,_intreq ; make TBE pending
;
; Enable, set RC and return to caller.
;
10$ moveq #1,d0 ; I/O not complete
jmp _LVOEnable(a6) ; enable interrupts
; return
;
; Resets serial read buffer
;
; Since this routine is called internally, it must NOT reference
; the I/O request.
;
cnop 0,4 ; align for 020+
cmd_Clear:
jsr _LVODisable(a6) ; disable interrupts
;
; Load registers
;
move.l vb_CurRBuf(a5),d0 ; get internal buffer ptr
move.l vb_CurRBufLen(a5),d1 ; and internal buffer len
lea.l i_BufPtr(pc),a0 ; get ptr internal control
;
; Initialize global buffer variables
;
move.l d0,(a0)+ ; store buffer ptr
move.l d0,(a0)+ ; set current input ptr
move.l d0,(a0)+ ; set current output ptr
add.l d1,d0 ; add buffer length
move.l d0,(a0)+ ; store ptr to end of buffer
clr.l (a0)+ ; clear byte cnt
move.l vb_CurBaud(a5),d0 ; get internal baud
lsr.l #4,d0 ; divide by 16
sub.l d0,d1 ; subtract from length
move.l d1,(a0) ; set threshold
;
; Enable, set RC and return.
;
moveq #0,d0 ; I/O complete
jmp _LVOEnable(a6) ; enable interrupts
; return
;
;
;
cnop 0,4 ; align for 020+
cmd_Invalid
move.b #IOERR_NOCMD,IO_ERROR(a1) ; set bad status
moveq #0,d0 ; I/O complete
rts ; return
;
; Returns number of bytes currently in internal buffer and
; current serial port status.
;
; NOTE: Not completely compatible with standard serial.device
; since it doesn't return the upper byte of IO_STATUS.
;
cnop 0,4 ; align for 020+
sdcmd_Query:
jsr _LVODisable(a6) ; disable interrupts
moveq #0,d0 ; clear d0
move.b _ciabpra,d0 ; get PR register
andi.b #~PRTMASK,d0 ; zap printer bits
;
; To use this Set SETDSR to one. This was done
; for a user whose DSR pin did not function.
;
IFNE SETDSR
andi.b #~(1<<CIAB_COMDSR),d0 ; set DSR
ENDC
;
;
;
move.w d0,IO_STATUS(a1) ; store status
move.l i_InCnt(pc),IO_ACTUAL(a1) ; byte left in buffer
;
; Enable, set RC and return.
;
moveq #0,d0 ; I/O complete
jmp _LVOEnable(a6) ; enable interrupts
; return
;
;
;
cnop 0,4 ; align for 020+
sdcmd_SetParams:
;
; Validate the read, write, and stop bit lengths.
;
moveq #8,d0 ; get char length
cmp.b IO_READLEN(a1),d0 ; 8 bit chars for read?
bne.b 40$ ; nope, branch
cmp.b IO_WRITELEN(a1),d0 ; 8 bit chars for write?
bne.b 40$ ; nope, branch
moveq #1,d0 ; get stop bits
cmp.b IO_STOPBITS(a1),d0 ; 1 stop bit?
bne.b 40$ ; nope, branch
;
; Get and validate the baud rate.
;
move.l IO_BAUD(a1),d1 ; get baud from I/O req
bne.b 20$ ; specified?
move.l vb_CurBaud(a5),d1 ; get current baud from base
bne.b 20$ ; specified?
move.l vb_DefBaud(a5),d1 ; get default baud from base
20$ cmpi.l #110,d1 ; too low?
blt.b 40$ ; error
cmpi.l #292000,d1 ; too high?
bgt.b 40$ ; error
;
; Get and validate the buffer length.
;
move.l IO_RBUFLEN(a1),d0 ; get buffer length
bne.b 30$ ; specified?
move.l vb_CurRBufLen(a5),d0 ; get current from base
bne.b 30$ ; specified?
move.l vb_DefRBufLen(a5),d0 ; get default from base
;
30$ bsr.b internalReset ; go init baud and buffer
35$ move.b d0,IO_ERROR(a1) ; set error code
bne.b 39$
;
; If the 7wire bit is not on, we will only use 3-wire protocol
;
moveq #0,d0 ; clear flag
btst #SERB_7WIRE,IO_SERFLAGS(a1) ; use 7wire handshaking?
beq.b 36$ ; nope, branch
moveq #1,d0 ; set flag
36$ move.b d0,Handshake ; store flag
;
; Set RC and return.
;
39$ moveq #0,d0 ; I/O complete
rts ; return
;
; Invalid parm detected.
;
40$ moveq #SerErr_InvParam,d0 ; set error
bra.b 35$ ; go return
;
; The NewStyleDevices Query command.
;
cnop 0,4 ; align for 020+
nscmd_DeviceQuery:
move.l IO_DATA(a1),a0
beq.b 10$
moveq #NSDEVICEQUERYRESULT_SIZE,d0
move.l d0,SIZEAVAILABLE(a0)
move.l d0,IO_ACTUAL(a1)
move.w #NSDEVTYPE_SERIAL,DEVICETYPE(a0)
move.l a1,-(sp)
lea.l cmdlist(pc),a1
move.l a1,SUPPORTEDCOMMANDS(a0)
move.l (sp)+,a1
10$ moveq #0,d0
rts
;
; Reset the buffer and baud rate
;
; Registers: D0 = Buffer length
; D1 = Baud rate
;
cnop 0,4 ; align for 020+
internalReset:
;
; Disable interrupts.
;
jsr _LVODisable(a6) ; disable interrupts
;
; Save buffer length and go set serper.
;
move.l d0,-(sp) ; save D0
move.l d1,d0 ; get baud rate
;
; Set serial period register
;
cmp.l vb_CurBaud(a5),d0 ; current baud = new baud?
beq.b 40$ ; yep, just exit
move.l d0,vb_CurBaud(a5) ; save new baud
move.l d0,d1 ; save again
lsl.l #3,d0 ; baud *= 8
sub.l d1,d0 ; baud -= saved baud
move.l #25000000,d1 ; get NTSC base
cmpi.b #50,PowerSupplyFrequency(a6) ; PAL machine?
bne.b 5$ ; nope, branch
move.l #24772416,d1 ; get PAL base
5$ cmpi.l #$FFFF,d0 ; Divide
ble.b 20$ ;
lsr.l #5,d0 ;
divu.w d0,d1 ;
andi.l #$FFFF,d1 ;
lsr.l #5,d1 ;
bra.b 30$ ;
cnop 0,4 ; align for 020+
20$ divu.w d0,d1 ;
30$ move.w d1,_serper ; set period value
40$
move.l (sp)+,d0 ; restore D0
;
; Determine if the buffer length is adequate for the selected CPS.
; If not, use 64K for the length.
;
move.l vb_CurBaud(a5),d1 ; get current baud
lsr.l #$3,d1 ; divide by 8
cmp.l d1,d0 ; buflen > CPS
bhi.b 10$ ; yep, branch
move.l #65536,d0 ; else use 64K
;
; Allocate new internal buffer
;
10$ cmp.l vb_CurRBufLen(a5),d0 ; len same as previous?
beq.b 11$ ; yep, so no need to alloc
move.l d0,d1 ; save length
move.l a1,-(sp) ; save registers
move.l d1,-(sp) ; save registers
moveq #MEMF_PUBLIC,d1 ; public memory
jsr _LVOAllocMem(a6) ; go allocate it
move.l (sp)+,d1 ; restore registers
move.l (sp)+,a1 ; restore registers
tst.l d0 ; did we get it?
beq.b 21$ ; if zero, error
bsr.b freeBuf ; go free previous buffer
move.l d0,vb_CurRBuf(a5) ; store new ptr
move.l d1,vb_CurRBufLen(a5) ; and length
bsr cmd_Clear ; go setup buffer
11$ moveq #0,d0 ; success
bra.b 22$ ; return
cnop 0,4 ; align for 020+
21$ moveq #SerErr_BufErr,d0 ; set error status
22$
;
; Enable and return to caller.
;
jmp _LVOEnable(a6) ; enable interrupts / return (D0 has status)
;
; Free internal buffer
;
cnop 0,4 ; align for 020+
freeBuf:
movem.l d0-d1/a0-a1,-(sp) ; save registers
move.l vb_CurRBuf(a5),d0 ; is one there?
beq.b 10$ ; no so branch
movea.l d0,a1 ; get ptr
move.l vb_CurRBufLen(a5),d0 ; get length
clr.l vb_CurRBuf(a5) ; clear
clr.l vb_CurRBufLen(a5) ; clear
jsr _LVOFreeMem(a6) ; free it
10$ move.l (sp)+,d0 ; restore registers
move.l (sp)+,d1 ; restore registers
move.l (sp)+,a0 ; restore registers
move.l (sp)+,a1 ; restore registers
rts ; return
;
; Checks CTS status and if clear generates a TBE interrupt or
; requeues the timer request.
;
; Entered from Exec using the MsgPort callback.
;
; Input: a6 = ExecBase
; Output: none
;
; No need to preserve d0/d1/a0/a1
;
cnop 0,4 ; align for 020+
timerRtn:
lea.l timerReq(pc),a1 ; get ptr to timer request
; This is the REMOVE macro
move.l (a1)+,a0
move.l (a1),a1 ; LN_PRED
move.l a0,(a1)
move.l a1,LN_PRED(a0)
;
; If we were breaking, reset adkcon.
;
lea.l _custom,a0 ; get ptr to custom regs
btst #ADKB_UARTBRK&7,adkconr(a0) ; were we breaking?
beq.b 10$ ; nope, skip reset
;
move.w #ADKF_UARTBRK,adkcon(a0) ; stop breaking
;
;
;
10$ tst.b Handshake(pc) ; are we handshaking?
beq.b 20$ ; nope, generate interrupt
btst #CIAB_COMCTS,_ciabpra ; clear to send?
beq.b 20$ ; yep, go start writing
;
lea.l timerReq(pc),a1 ; get ptr to timer request
move.l #1000,IOTV_TIME+TV_MICRO(a1) ; wait for .001 seconds
jsr _LVOSendIO(a6) ; go queue it
bra.b 30$ ; branch to return
;
; CTS is clear so generate TBE interrupt to restart writing
;
cnop 0,4 ; align for 020+
20$ move.w #INTF_SETCLR|INTF_TBE,intreq(a0) ; set TBE interrupt
30$ rts ; return
;
; Non serial interrupt
;
; a0 - custom chips base
; a1 - is_Data
cnop 0,4 ; align for 020+
level1n:
movem.l d0-d1/a0-a1/a5-a6,-(sp) ; save registers
lea _custom,a0 ; get ptr to custom regs
move.w intenar(a0),d1 ; get enabled interrupts
and.w intreqr(a0),d1 ; and in requested interrupts
movea.l SysBase(pc),a6 ; get ExecBase
;
btst #INTB_DSKBLK,d1 ; Disk block done?
beq.b 10$ ; nope, branch
;
movem.l IVDSKBLK(a6),a1/a5 ; get data and code ptrs
pea.l 20$(pc) ; push return address
jmp (a5) ; jump to routine
;
10$ btst #INTB_SOFTINT,d1 ; software interrupt?
beq.b 20$ ; nope, branch
;
movem.l IVSOFTINT(a6),a1/a5 ; get data and code ptrs
jsr (a5) ; jump to routine
;
20$ move.l (sp)+,d0 ; restore registers
move.l (sp)+,d1 ; restore registers
move.l (sp)+,a0 ; restore registers
move.l (sp)+,a1 ; restore registers
move.l (sp)+,a5 ; restore registers
move.l (sp)+,a6 ; restore registers
rte ; return
;
; Level 1 interrupt handler
;
; a1 - is_Data
;
cnop 0,4 ; align for 020+
level1:
btst #INTB_INTEN&7,_intenar ; interrupts enabled?
beq.b 35$ ; nope, ignore
IFEQ NEWCODE
btst #INTB_SOFTINT,_intreqr+1 ; software interrupt?
bne.b level1n ; nope, branch
ENDC
btst #INTB_TBE,_intreqr+1 ; xmit buffer empty?
beq.b level1n ; nope, invoke old handler
;
; Handle "Transmit Buffer Empty" interrupt (write)
;
move.w #INTF_TBE,_intreq ; clear interrupt
move.l d0,-(sp) ; save D0 (faster than MOVEM)
move.l a0,-(sp) ; save A0 (faster than MOVEM)
;
; If we're not handshaking, bypass it.
;
10$ tst.b Handshake(pc) ; are we handshaking?
beq.b 20$ ; nope, skip CTS test
btst #CIAB_COMCTS,_ciabpra ; clear to send?
bne.b 40$ ; nope, branch
;
; If cw_Length goes negative here, we are either done with a
; request or we were called as a result of a fake interrupt
; to force us to get the next request going.
;
20$ subq.l #1,cw_Length ; decr write length
blt.b 60$ ; < zero, done, branch
;
; Currently processing a request.
;
movea.l cw_Buffer(pc),a0 ; get buffer ptr
move.w #256,d0 ; set stop bit
move.b (a0)+,d0 ; get next byte
move.l a0,cw_Buffer ; store buffer ptr
move.w d0,_serdat ; store in serdat reg
;
30$ movea.l (sp)+,a0 ; restore registers
move.l (sp)+,d0 ; restore registers
35$ rte ; return
;
; Queue a timer request to recheck CTS status
;
40$ movem.l d1/a1/a6,-(sp) ; save registers
lea.l timerReq(pc),a1 ; get ptr to timer request
move.l #1000,IOTV_TIME+TV_MICRO(a1) ; wait for .001 seconds
movea.l SysBase(pc),a6 ; get ExecBase
jsr _LVOSendIO(a6) ; queue the request
move.l (sp)+,d1 ; restore registers
move.l (sp)+,a1 ; restore registers
move.l (sp)+,a6 ; restore registers
bra.b 30$ ; go return
;
; There aren't anymore requests, so clear and exit
;
cnop 0,4 ; align for 020+
50$ clr.l cw_Length-Start(a6) ; clear length
clr.l cw_IOReq-Start(a6) ; clear
move.l (sp)+,d1 ; restore registers
move.l (sp)+,a1 ; restore registers
move.l (sp)+,a6 ; restore registers
bra.b 30$ ; go return
;
; Write request completed
;
cnop 0,4 ; align for 020+
60$ movem.l d1/a1/a6,-(sp) ; save registers
;
move.l cw_IOReq(pc),d0 ; active I/O request?
beq.b 70$ ; nope, branch
;
; Reply it and setup for next
;
movea.l d0,a1 ; get I/O request
andi.b #~(1<<IOSERB_ACTIVE),IO_FLAGS(a1) ; no longer active
clr.b IO_ERROR(a1) ; no error
movea.l SysBase(pc),a6 ; get ExecBase
jsr _LVOReplyMsg(a6) ; return I/O request
;
70$ lea.l Start(pc),a6 ; get section base
;
lea.l writeQ(pc),a1 ; get ptr to write queue
move.l (a1),a0 ; get head of list
move.l (a0),d0 ; get successor
beq.b 50$ ; end of list? yep, branch
;
; Remove the node from the list
;
move.l d0,(a1) ; make new head
exg.l d0,a0 ; swap nodes
move.l a1,LN_PRED(a0) ; store predecessor
;
move.l d0,a1 ; get I/O request
;
; If it's a BREAK, then branch to process as such.
;
cmpi.w #SDCMD_BREAK,IO_COMMAND(a1) ; BREAK command?
beq.b 100$ ; yep, branch
;
; Check for absolute length
;
move.l IO_DATA(a1),a0 ; get data ptr
moveq #-1,d1 ; get value
move.l IO_LENGTH(a1),d0 ; get length
cmp.l d1,d0 ; length = -1?
bne.b 90$ ; nope, skip scan
;
; Scan the data for null to calc the length
;
move.l a0,d1 ; save data ptr
80$ tst.b (a0)+ ; does it equal 0? (first byte)
beq.b 81$ ; yep, finish
tst.b (a0)+ ; does it equal 0? (second byte)
beq.b 81$ ; yep, finish
tst.b (a0)+ ; does it equal 0? (third byte)
beq.b 81$ ; yep, finish
tst.b (a0)+ ; does it equal 0? (fourth byte)
bne.b 80$ ; nope, loop
81$
suba.l d1,a0 ; calc # of bytes
move.l a0,d0 ; xfer
movea.l d1,a0 ; get data ptr
;
90$ move.l d0,IO_ACTUAL(a1) ; go ahead and set it
move.b IO_FLAGS(a1),d1
andi.b #~(1<<IOSERB_QUEUED),d1 ; no longer queued
ori.b #1<<IOSERB_ACTIVE,d1 ; make it active
move.b d1,IO_FLAGS(a1)
move.l d0,cw_Length-Start(a6) ; store length
move.l a0,cw_Buffer-Start(a6) ; store buffer ptr
move.l a1,cw_IOReq-Start(a6) ; store I/O request ptr
;
move.l (sp)+,d1 ; restore registers
move.l (sp)+,a1 ; restore registers
move.l (sp)+,a6 ; restore registers
bra 10$ ; go start request
;
; Start the BREAK.
;
cnop 0,4 ; align for 020+
100$ move.b IO_FLAGS(a1),d1
andi.b #~(1<<IOSERB_QUEUED),d1 ; no longer queued
ori.b #1<<IOSERB_ACTIVE,d1 ; make it active
move.b d1,IO_FLAGS(a1)
clr.l cw_Length-Start(a6) ; clear length
move.l a1,cw_IOReq-Start(a6) ; store I/O request ptr
move.w #ADKF_SETCLR|ADKF_UARTBRK,_adkcon ; start break
move.l IO_BRKTIME(a1),d0 ; get break time
lea.l timerReq(pc),a1 ; get ptr to timer request
move.l d0,IOTV_TIME+TV_MICRO(a1) ; set the timeout
movea.l SysBase(pc),a6 ; get ExecBase
jsr _LVOSendIO(a6) ; queue the request
move.l (sp)+,d1 ; restore registers
move.l (sp)+,a1 ; restore registers
move.l (sp)+,a6 ; restore registers
bra 30$ ; go exit
;
; Non serial interrupt
;
; a1 - custom chips base
;
cnop 0,4 ; align for 020+
level5n:
movem.l d0-d1/a0-a1/a5-a6,-(sp) ; save registers
lea _custom,a0 ; get ptr to custom regs
move.w intenar(a0),d1 ; get enabled interrupts
and.w intreqr(a0),d1 ; and in requested interrupts
movea.l SysBase(pc),a6 ; get ExecBase
btst #INTB_DSKSYNC,d1 ; Disk synchronized?
beq.b 10$ ; nope, branch
movem.l IVDSKSYNC(A6),a1/a5 ; get data and code ptrs
jsr (a5) ; branch to routine
10$ move.l (sp)+,d0 ; restore registers
move.l (sp)+,d1 ; restore registers
move.l (sp)+,a0 ; restore registers
move.l (sp)+,a1 ; restore registers
move.l (sp)+,a5 ; restore registers
move.l (sp)+,a6 ; restore registers
rte ; return
;
; Default Level 5 handler
;
; a1 - custom chips base
;
cnop 0,4 ; align for 020+
level5:
btst #INTB_INTEN&7,_intenar ; interrupts enabled?
beq.b 45$ ; nope, ignore
btst #INTB_RBF&7,_intreqr ; receive buffer full?
beq.b level5n ; nope, invoke old handler
;
move.l a0,-(sp) ; save registers
move.l a1,-(sp) ; save registers
lea _custom,a1 ; get ptr to custom regs
;
10$ tst.b serdatr(a1) ; Overrun?
bpl.b 20$ ; nope, branch
;
; We've missed some data, so set overrun flag.
;
addq.b #1,Overrun ; set overrun flag
20$ movea.l i_BufIn(pc),a0 ; get current ptr
move.b serdatr+1(a1),(a0)+ ; store received byte
move.w #INTF_RBF,intreq(a1) ; clear RBF interrupt
addq.l #1,i_InCnt ; incr bytes in buffer
;
cmpa.l i_BufEnd(pc),a0 ; hit end of buffer?
beq.b 60$ ; yep, branch
30$ move.l a0,i_BufIn ; store input ptr
;
subq.l #1,i_Thresh ; close to full buffer?
beq.b 70$ ; yep, branch
;
40$ btst #INTB_RBF&7,intreqr(a1) ; receive buffer full?
bne.b 10$ ; yep, go get another byte
;
movea.l (sp)+,a1 ; restore registers
movea.l (sp)+,a0 ; restore registers
45$ rte ; return
;
; Hit physical end of buffer, so wrap to the start of the buffer.
;
cnop 0,4 ; align for 020+
60$ movea.l i_BufPtr(pc),a0 ; get buffer ptr
bra.b 30$ ; continue
;
; Hit buffer threshold, so tell other end not to send any more
; data.
;
cnop 0,4 ; align for 020+
70$ tst.b Handshake(pc) ; are we handshaking?
beq.b 40$ ; nope, skip RTS
ori.b #1<<CIAB_COMRTS,_ciabpra ; block further input
bra.b 40$ ; go return
;
;
; a1 - IS_DATA
; a5 - jump vector register
;
cnop 0,4 ; align for 020+
level2:
;
; If there's nothing in the buffer, there's no point in going
; any further.
;
tst.l i_InCnt(pc) ; anything in the buffer?
bne.b 20$ ; yep, branch
10$ moveq #0,d0 ; set Z flag
rts ; return
;
; If we've been "disabled" then get out.
;
20$ tst.b disableRead(pc) ; internally disabled?
bge.b 10$ ; yep, get out of here
lea.l Start(pc),a5 ; get base
;
; If we have an active request, branch down and try to fulfill it.
;
move.l cr_IOReq(pc),d0 ; get and test active I/O
bne.b 30$ ; nzero, active, branch
;
; Get first node in list and test if empty.
;
move.l (a1),a0 ; get head of list
move.l (a0),d0 ; get successor
beq.b 10$ ; end of list? yep, branch
;
; Remove the node from the list
;
move.l d0,(a1) ; make new head
exg.l d0,a0 ; swap nodes
move.l a1,LN_PRED(a0) ; store predecessor
;
; Setup fields for processing a read request
;
movea.l d0,a1 ; get I/O request
move.b IO_FLAGS(a1),d1
andi.b #~(1<<IOSERB_QUEUED),d1 ; no longer queued
ori.b #1<<IOSERB_ACTIVE,d1 ; make it active
move.b d1,IO_FLAGS(a1)
move.l a1,cr_IOReq-Start(a5) ; store I/O request
move.l IO_DATA(a1),cr_OutPtr-Start(a5) ; get/set output ptr
move.l IO_LENGTH(a1),cr_Length-Start(a5) ; get/set output count
;
; Process an active I/O request
;
30$ movea.l SysBase(pc),a6 ; get ExecBase
move.l d0,a1 ; get I/O request
bsr.b copyData ; go copy 'em
tst.l d0 ; done with request?
beq.b 10$ ; nope, branch
;
; the request has been satisfied, so return it.
;
clr.l cr_IOReq-Start(a5) ; clear request
andi.b #~(1<<IOSERB_ACTIVE),IO_FLAGS(a1) ; no longer active
jsr _LVOReplyMsg(a6) ; return I/O
bra.b 10$
;
; Registers:
; Entry: A1 Ptr to I/O Request
; A6 ExecBase
; Exit: D0 ZERO - request not done, do not reply it
; NZERO - request done, reply it
;
cnop 0,4 ; align for 020+
copyData:
;
; Process an active I/O request (or fall through from above)
;
movem.l d2-d5/a1-a5,-(sp) ; save registers
lea.l Start(pc),a5 ; get base
movea.l a1,a4 ; get I/O request
;
movea.l i_BufOut(pc),a2 ; get current bufout ptr
movea.l cr_OutPtr(pc),a3 ; get current output ptr
move.l cr_Length(pc),d2 ; get current bytes needed
move.l i_InCnt(pc),d3 ; get current bytes in buffer
IFNE STATS
movem.l d0-d7/a0-a6,-(sp)
lea.l .stata,a0
move.l d2,.statb
lea.l .statb,a1
jsr KPrintF
bra.b .endstat
.statb dc.l 0
dc.l 0
.stata dc.b 'Length to copy %8ld\n',0
cnop 0,4
.endstat
movem.l (sp)+,d0-d7/a0-a6
ENDC
;
; If we don't have enough bytes to satisfy the request,
; set the length to the number of bytes we do have.
;
cmp.l d2,d3 ; enuf to satisfy request?
bge.b 15$ ; yep, so branch
move.l d3,d2 ; # to copy = # in buffer
bra.b 15$ ; branch to loop entry
;
; Start of copy loop.
;
cnop 0,4 ; align for 020+
10$ movea.l i_BufPtr(pc),a2 ; reset bufout to start
;
; Entry point of copy loop.
;
15$ move.l d2,d3 ; xfer # of bytes to copy
;
; If the copy will extend past the end of the buffer, we can
; only copy the number of bytes to the end this go around.
;
move.l i_BufEnd(pc),d0 ; get ptr to end of buf
sub.l a2,d0 ; calc # of bytes to end
cmp.l d0,d3 ; # to copy < # to end?
blt.b 20$ ; yep, branch
move.l d0,d3 ; get bytes to end
;
; Registers:
;
; A2 = pointer from which data will be copied
; A3 = pointer to which data will be copied
; D2 = number of bytes that need to be copied
; D3 = number of bytes to copy this iteration
;
20$ btst #SERB_EOFMODE,IO_SERFLAGS(a4) ; EOFMODE requested?
beq.b 30$ ; nope, just go copy
;
; EOFMODE was specified so copy characters 1 at a time until
; we hit an EOF character, the output butter has filled, or
; the input buffer has drained.
;
move.l d3,d0 ; get length
;
lea.l IO_TERMARRAY+7(a4),a1 ; get termarry ptr
21$ move.b (a2)+,d1 ; get byte
; This way the term char IS copied (if term char is not copied eofmode will fail)
move.b d1,(a3)+ ; put in output buffer
;
subq.l #7,a1
cmp.b (a1)+,d1 ; found term char?
bge.b 22$ ; possibly, branch
cmp.b (a1)+,d1 ; found term char?
bge.b 22$ ; possibly, branch
cmp.b (a1)+,d1 ; found term char?
bge.b 22$ ; possibly, branch
cmp.b (a1)+,d1 ; found term char?
bge.b 22$ ; possibly, branch
cmp.b (a1)+,d1 ; found term char?
bge.b 22$ ; possibly, branch
cmp.b (a1)+,d1 ; found term char?
bge.b 22$ ; possibly, branch
cmp.b (a1)+,d1 ; found term char?
bge.b 22$ ; possibly, branch
cmp.b (a1),d1 ; found term char?
bgt.b 23$ ; nope, branch
22$ beq.b 24$ ; term char found?
;
; Didn't find a term character, so continue with the copy loop
;
23$
subq.l #1,d0 ; decr length counter
bne.b 21$ ; continue if more
bra.b 40$ ; done with copy, branch
;
; We've found a termination character.
;
cnop 0,4 ; align for 020+
24$ clr.l cr_Length-Start(a5) ; done with request
bra.b 50$ ; branch
cnop 0,4 ; align for 020+
;
; EOFMODE not specified, so just do a bulk copy.
;
; XXX POSSIBLE SPEEDUP XXX
;
; For short copies it would be quicker to have a simple inline
; loop, but what's short??? It would be different by CPU.
;
30$
move.l d3,d0 ; get length
movea.l a3,a1 ; where to put it
movea.l a2,a0 ; where to get it
; NB: buffer size is multiple of 64bytes (not contents size)
; most copys are 1 or 2 bytes, max copy usually seen is 256 bytes
jsr _LVOCopyMem(a6) ; we use copymemquicker patch by *Art
adda.l d3,a2 ; update bufout
adda.l d3,a3 ; update outptr
;
; Fall through and entered from EOFMODE loop
;
; If the following calculation results in a value greater than
; zero, then we have a buffer wrap and need to process the
; remaining bytes at the beginning of the buffer.
;
40$ sub.l d3,d2 ; calc bytes left to copy
bgt 10$ ; >0, more to copy, branch
;
;
;
50$ move.l a3,d1 ; get outptr
sub.l cr_OutPtr(pc),d1 ; calc length
move.l a3,cr_OutPtr-Start(a5) ; update outptr
;
; Update output ptr and I/O request
;
move.l a2,i_BufOut-Start(a5) ; store bufout ptr
add.l d1,IO_ACTUAL(a4) ; update I/O request
;
; Update number of bytes left in the buffer.
;
sub.l d1,i_InCnt-Start(a5) ; calc bytes left in buffer
;
; If the threshold becomes positive here, then, if requested,
; tell the other end that it's okay to start sending more data.
;
add.l d1,i_Thresh-Start(a5) ; calc thresh and test
ble.b 60$ ; <= 0, need more data, skip
tst.b Handshake(pc) ; are we handshaking?
beq.b 60$ ; nope, skip RTS
andi.b #~(1<<CIAB_COMRTS),_ciabpra ; ready to recieve more data
;
; Set error if we've had any overruns.
;
60$ tst.b Overrun(pc) ; did overrun occur?
beq.b 70$ ; nope, branch
clr.b Overrun-Start(a5) ; reset overrun flag
move.b #SerErr_LineErr,IO_ERROR(a4) ; set error code
;
; Calc number of bytes left to copy. If the result is greater
; than zero, then we have more to copy so return an incomplete
; status.
;
70$ moveq #0,d0 ; assume I/O incomplete
sub.l d1,cr_Length-Start(a5) ; update length and test
bgt.b 90$ ; >0, more to do, branch
;
; We've completed the I/O request either by copying the requested
; of bytes or by finding an EOFMODE character, so return a "reply"
; status.
;
moveq #1,d0 ; indicate reply
;
; Restore and exit
;
90$ move.l (sp)+,d2 ; restore registers
move.l (sp)+,d3 ; restore registers
move.l (sp)+,d4 ; restore registers
move.l (sp)+,d5 ; restore registers
move.l (sp)+,a1 ; restore registers
move.l (sp)+,a2 ; restore registers
move.l (sp)+,a3 ; restore registers
move.l (sp)+,a4 ; restore registers
move.l (sp)+,a5 ; restore registers
rts ; return (status in D0)
;
; Align data
;
CNOP 0,4
;
;
;
Init:
DC.L sizeof_Base8n1
DC.L funcTab
DC.L dataTab
DC.L InitRoutine
;
;
;
funcTab:
DC.W -1
DC.W dev_Open-funcTab
DC.W dev_Close-funcTab
DC.W dev_Expunge-funcTab
DC.W dev_Null-funcTab
DC.W dev_BeginIO-funcTab
DC.W dev_AbortIO-funcTab
DC.W -1
;
;
;
dataTab:
INITBYTE LN_TYPE,NT_DEVICE
INITLONG LN_NAME,Name
INITBYTE LIB_FLAGS,LIBF_SUMUSED|LIBF_CHANGED
INITWORD LIB_VERSION,VERSION
INITWORD LIB_REVISION,REVISION
INITLONG LIB_IDSTRING,IdString
DC.W 0
;
; String Constants
;
miscresource:
DC.B "misc.resource",0
timerdevice:
DC.B "timer.device",0
intuitlib:
DC.B "intuition.library",0
Name:
DC.B "8n1.device",0
IdString:
VSTRING
;
; End of checksummed area. (Realigns data too!)
;
ENDTag:
CNOP 0,4
;
; Global SysBase (Use instead of _AbsExecBase for speed)
;
SysBase:
DC.L 0
;
; Internal buffer tracking (DO NOT CHANGE THE ORDER!!!!)
;
i_BufPtr:
DC.L 0
i_BufIn:
DC.L 0
i_BufOut:
DC.L 0
i_BufEnd:
DC.L 0
i_InCnt:
DC.L 0
i_Thresh:
DC.L 0
;
; Used while processing a read request.
;
cr_IOReq:
DC.L 0
cr_OutPtr:
DC.L 0
cr_Length:
DC.L 0
;
; List head for read requests
;
readQ:
DC.L readQ+MLH_TAIL
DC.L 0
DC.L readQ
;
; Write control.
;
cw_Length:
DC.L 0
cw_Buffer:
DC.L 0
cw_IOReq:
DC.L 0
;
; List head for write requests
;
writeQ:
DC.L writeQ+MLH_TAIL
DC.L 0
DC.L writeQ
;
;
;
timerPort:
DC.L 0 ; LN_SUCC
DC.L 0 ; LN_PRED
DC.B NT_MSGPORT ; LN_TYPE
DC.B 0 ; LN_PRI
DC.L 0 ; LN_NAME
DC.B 3 ; MP_FLAGS (undoc'ed)
DC.B 0 ; MP_SIGBIT
DC.L timerRtn ; MP_SIGTASK
DC.L timerPort+MP_MSGLIST+LH_TAIL ; LH_HEAD
DC.L 0 ; LH_TAIL
DC.L timerPort+MP_MSGLIST ; LH_TAILPRED
DC.B 0 ; LH_TYPE
DC.B 0 ; LH_pad
DC.W 0 ; long align
;
;
;
timerReq:
DC.L 0 ; LN_SUCC
DC.L 0 ; LN_PRED
DC.B NT_MESSAGE ; LN_TYPE
DC.B 0 ; LN_PRI
DC.L 0 ; LN_NAME
DC.L timerPort ; MN_REPLYPORT
DC.W IOTV_SIZE ; MN_LENGTH
DC.L 0 ; IO_DEVICE
DC.L 0 ; IO_UNIT
DC.W TR_ADDREQUEST ; IO_COMMAND
DC.B 0 ; IO_FLAGS
DC.B 0 ; IO_ERROR
DC.L 0 ; TV_SECS
DC.L 0 ; TV_MICROS
;
;
;
VBInterrupt:
DC.L 0 ; LN_SUCC
DC.L 0 ; LN_PRED
DC.B NT_INTERRUPT ; LN_TYPE
DC.B 0 ; LN_PRI
DC.L Name ; LN_NAME
DC.L readQ ; IS_DATA
DC.L level2 ; IS_CODE
;
; Global flags
;
Overrun:
DC.B 0
Handshake:
DC.B 1
disableRead:
DC.B -1
CNOP 0,4
cmdlist:
dc.w CMD_RESET
dc.w CMD_READ
dc.w CMD_WRITE
dc.w CMD_CLEAR
dc.w CMD_FLUSH
dc.w SDCMD_QUERY
dc.w SDCMD_BREAK
dc.w SDCMD_SETPARAMS
dc.w NSCMD_DEVICEQUERY
dc.w 0
;
;
;
END